package com.dannypatterson.as3p2p {
	
	import com.dannypatterson.as3p2p.events.ConnectionEvent;
	import com.dannypatterson.as3p2p.events.PeerDataEvent;
	
	import flash.events.EventDispatcher;
	import flash.media.Camera;
	import flash.media.Microphone;
	import flash.media.SoundCodec;
	import flash.media.Video;
	import flash.net.NetConnection;
	import flash.net.NetStream;
	import flash.net.NetStreamInfo;
	import flash.utils.setTimeout;
	
	/**
	 * This class holds the actual connection to the peer.  It contains the
	 * incoming and outgoing streams and all communication goes through this class.
	 * 
	 * @author Danny Patterson
	 */
	public class PeerConnection extends EventDispatcher {
		
		private var stratusConnection:NetConnection;
		private var me:Peer;
		private var peer:Peer;
		private var channel:String;
		private var outgoingStream:NetStream;
		private var incomingStream:NetStream;
		
		public var video:Video;
		public var camera:Camera;
		public var microphone:Microphone;
		
		/**
		 * @return (String) Returns the peer's registered name.
		 */
		public function get peerName():String {
			return (peer.name != null) ? peer.name : "peer";
		}
		
		/**
		 * @return (String) Returns my registered name.
		 */
		public function get myName():String {
			return (me.name != null) ? me.name : "me";
		}
		
		/**
		 * @return (NetStreamInfo)
		 */
		public function get incomingNetStreamInfo():NetStreamInfo {
			return (incomingStream != null) ? incomingStream.info : null;
		}
		
		/**
		 * @return (NetStreamInfo)
		 */
		public function get outgoingNetStreamInfo():NetStreamInfo {
			return (outgoingStream != null) ? outgoingStream.info : null;
		}
		
		/**
		 * This setter enables or disables the video for the incoming stream and
		 * attaches the video to the display.
		 */
		public function set playbackVideo(value:Boolean):void {
			if(value) {
				incomingStream.receiveVideo(true);
				if(video != null) {
					video.attachNetStream(incomingStream);
				}
			}else {
				incomingStream.receiveVideo(false);
				if(video != null) {
					video.attachNetStream(null);
					video.clear();
				}
			}
		}
		
		/**
		 * This setter enables or disables the audio for the incoming stream.
		 */
		public function set playbackAudio(value:Boolean):void {
			if(value) {
				incomingStream.receiveAudio(true);
			}else {
				incomingStream.receiveAudio(false);
			}
		}
		
		/**
		 * This setter enables or disables the broadcast of the user's camera.
		 */
		public function set broadcastVideo(value:Boolean):void {
			if(value) {
				if(camera == null) {
					camera = Camera.getCamera();
					if(camera != null) camera.setMode(320, 240, 15);
				}
				if(camera != null) {
					outgoingStream.attachCamera(camera);
				}
			}else {
				outgoingStream.attachCamera(null);
			}
		}
		
		/**
		 * This setter enables or disables the broadcast of the user's mic.
		 */
		public function set broadcastAudio(value:Boolean):void {
			if(value) {
				if(microphone == null) {
					microphone = Microphone.getMicrophone();
					if(microphone != null) microphone.codec = SoundCodec.SPEEX;
				}
				if(microphone != null) {
					outgoingStream.attachAudio(microphone);
				}
			}else {
				outgoingStream.attachAudio(null);
			}
		}
		
		
		/**
		 * @param stratusConnection (NetConnection)
		 * @param me (Peer)
		 * @param peer (Peer)
		 * @param channel (String)
		 */
		public function PeerConnection(stratusConnection:NetConnection, me:Peer, peer:Peer, channel:String) {
			this.stratusConnection = stratusConnection;
			this.me = me;
			this.peer = peer;
			this.channel = channel;
			incomingStream = new NetStream(stratusConnection, peer.id);
			incomingStream.receiveAudio(false);
			incomingStream.receiveVideo(false);
			incomingStream.play(channel);
			incomingStream.client = this;
			outgoingStream = new NetStream(stratusConnection, NetStream.DIRECT_CONNECTIONS);
			var c:Object = new Object();
			c.onPeerConnect = function(caller:NetStream):Boolean {
				setTimeout(onDelayedPeerUpdate, 1000);
				return true;
			}
			outgoingStream.client = c;
			outgoingStream.publish(channel);
		}
		
		
		private function onDelayedPeerUpdate():void {
			outgoingStream.send("onPeerUpdate", me);
		}
		
		
		/**
		 * This is a callback that handles a peer sending their updated peer
		 * object.  This is useful if you don't know the name of your peer.
		 * 
		 * @param peer (Peer) The updated Peer object sent from your connected peer.
		 */
		public function onPeerUpdate(peer:Peer):void {
			this.peer.name = peer.name;
			dispatchEvent(new ConnectionEvent(ConnectionEvent.PEER_UPDATED));
		}
		
		/**
		 * This is a callback that handles a peer sending a data object.
		 * 
		 * @param data (Object) The data object sent from your Peer.
		 */
		public function onIncomingData(data:Object):void {
			dispatchEvent(new PeerDataEvent(PeerDataEvent.PEER_DATA_RECEIVED, data));
		}
		
		/**
		 * This method sends a data object to your peer.
		 * 
		 * @param data (Object) The data object to send to your peer.
		 */
		public function sendData(data:Object):void {
			outgoingStream.send("onIncomingData", data);
		}

	}
	
}